﻿using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Zijian666
{
    /// <summary>
    /// 套接字字符串读取器
    /// </summary>
    internal class SocketStringReader
    {
        /// <summary>
        /// 自定义分隔符
        /// </summary>
        public const string SPLITION = "\0\0\f\f\0\f";
        // 字符串缓存
        private readonly StringBuilder _stringBuilder = new();
        // 套接字实例
        private readonly Socket _socket;
        // 事件回调
        private readonly EventHandler<string> _receiver;
        // 取消标识
        private readonly CancellationToken _token;
        // 缓冲区
        private readonly byte[] _buffer;

        // 已处理字符串位置
        private int _position = 0;

        /// <summary>
        /// 构造函数
        /// </summary>
        public SocketStringReader(Socket socket, EventHandler<string> receiver, CancellationToken token)
        {
            _socket = socket ?? throw new ArgumentNullException(nameof(socket));
            _receiver = receiver ?? throw new ArgumentNullException(nameof(receiver));
            _token = token;
            _buffer = new byte[4 * 1024];
            BeginReceive();
        }

        // 开始接收数据
        private void BeginReceive() => _ = _socket.BeginReceive(_buffer, 0, 4 * 1024, SocketFlags.None, EndReceive, null);

        // 接收数据回调
        private void EndReceive(IAsyncResult ar)
        {
            _token.ThrowIfCancellationRequested();
            try
            {
                var byteCount = _socket.EndReceive(ar);
                _stringBuilder.Append(Encoding.UTF8.GetString(_buffer, 0, byteCount));
                if (byteCount > 0)
                {
                    for (var i = _position; i < _stringBuilder.Length; i++)
                    {
                        if (IsSplited(ref i))
                        {
                            var s = _stringBuilder.ToString(0, i - SPLITION.Length);
                            _stringBuilder.Remove(0, i);
                            _position = 0;
                            i = 0;
                            if (s.Length > 0)
                            {
                                _receiver(_socket, s);
                            }
                        }
                    }
                    _position = _stringBuilder.Length;
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(ex);
            }
            finally
            {
                if (_socket.Connected)
                {
                    BeginReceive();
                }
            }
        }

        // 是否已经读到分隔符的位置
        private bool IsSplited(ref int i)
        {
            if (_stringBuilder[i] != SPLITION[0])
            {
                return false;
            }
            i++;
            for (var j = 1; j < SPLITION.Length; j++, i++)
            {
                if (_stringBuilder[i] != SPLITION[j])
                {
                    return false;
                }
            }
            return true;
        }
    }
}
